#include <math.h>
#include "3d_camera.h"

/*
    *  参数:
    *      width,height: 屏幕宽高
    *      far: 远端视野消失点,即模型放在这个距离时在屏幕就只剩下一个点了
    *      near: 近端穿过镜头消失点
    */
D3Camera::D3Camera(int32_t width, int32_t height, int32_t far, int32_t near)
{
    this->width = width;
    this->height = height;
    this->widthHalf = width / 2;
    this->heightHalf = height / 2;
    this->far = far;
    this->near = near;
}

bool D3Camera::IsInside(int32_t x, int32_t y)
{
    if (x < 0 || x >= width || y < 0 || y >= height)
        return false;
    return true;
}

bool D3Camera::IsInside(int32_t *result, int32_t len)
{
    int32_t sum[6] = {0};
    for (int32_t i = 0; i < len; i++)
    {
        if (result[i] & D3C_RESULT_OUT_OF_TOP)
            sum[0] += 1;
        if (result[i] & D3C_RESULT_OUT_OF_BOTTOM)
            sum[1] += 1;
        if (result[i] & D3C_RESULT_OUT_OF_LEFT)
            sum[2] += 1;
        if (result[i] & D3C_RESULT_OUT_OF_RIGHT)
            sum[3] += 1;
        if (result[i] & D3C_RESULT_OUT_OF_NEAR)
            sum[4] += 1;
        if (result[i] & D3C_RESULT_OUT_OF_FAR)
            sum[5] += 1;
    }
    if (sum[0] == len || sum[1] == len ||
        sum[2] == len || sum[3] == len ||
        sum[4] == len || sum[5] == len)
        return false;
    return true;
}

int32_t D3Camera::GetWidth()
{
    return width;
}

int32_t D3Camera::GetHeight()
{
    return height;
}

// void D3Camera::SetWidth(int32_t width)
// {
//     this->width = width;
// }

// void D3Camera::SetHeight(int32_t height)
// {
//     this->height = height;
// }

/*
    *  模型与屏幕距离为0时的显示大小倍数
    *  例如模型宽10像素,与屏幕0距离,而该倍数为1,则在屏幕显示也是10像素
    *  数值必须大于0
    */
float D3Camera::GetZoom()
{
    return zoom;
}

void D3Camera::SetZoom(float zoom)
{
    if (zoom > 0)
        this->zoom = zoom;
}

/*
*  透视投影,把三维坐标映射到相机屏幕
*  参数:
*       xyz: 待映射三维坐标(需相对于相机坐标系,若为原始空间坐标,需先camera->Transfer(xyz)处理)
*       retXY: 返回的屏幕坐标,屏幕左上角为(0,0),注意返回坐标包含负数等超出屏幕范围的坐标
*       retDepth: 返回深度,范围0.0~1.0
*  返回: 解析见 D3CameraResult 定义
*/
int32_t D3Camera::Project(float xyz[3], int32_t retXY[2], float retDepth[1])
{
    int32_t ret = D3C_RESULT_INSIDE;
    ProjectArray(xyz, retXY, retDepth, 1);
    //入屏结果,叠buff
    if (retXY[1] < 0)
        ret |= D3C_RESULT_OUT_OF_TOP;
    else if (retXY[1] >= height)
        ret |= D3C_RESULT_OUT_OF_BOTTOM;
    if (retXY[0] < 0)
        ret |= D3C_RESULT_OUT_OF_LEFT;
    else if (retXY[0] >= width)
        ret |= D3C_RESULT_OUT_OF_RIGHT;
    if (xyz[0] < near)
        ret |= D3C_RESULT_OUT_OF_NEAR;
    else if (xyz[0] > far)
        ret |= D3C_RESULT_OUT_OF_FAR;
    return ret;
}

//批量处理模式,注意count为坐标点个数
void D3Camera::ProjectArray(float *xyz, int32_t *retXY, float *retDepth, int32_t count)
{
    for (int32_t i = 0; i < count; i++)
    {
        //深度
        if (xyz[0] > far)
            retDepth[0] = zoom;
        else
            retDepth[0] = xyz[0] / (far - near) * zoom;

        //横纵坐标
        retXY[0] = widthHalf - xyz[1] * (zoom - retDepth[0]);
        retXY[1] = heightHalf - xyz[2] * (zoom - retDepth[0]);

        xyz += 3;
        retXY += 2;
        retDepth += 1;
    }
}
